package server.rest;

import java.io.IOException;
import java.io.OutputStream;
import java.util.HashMap;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang.StringUtils;
import org.apache.http.protocol.HTTP;
import common.KeyValue;
import common.Utils;
import common.Utils.ByteArrayStreamContent;

public class HttpContext
{
	HttpServletRequest request;
	HttpServletResponse response;
	HashMap<String,String> parameters;
	String responseText;
	String responseContentType;
	int responseStatusCode=HttpServletResponse.SC_OK;
	public boolean userResponse = false;
	HttpServer server;
	int contentLength;
	boolean compressed;
	 
	public HttpContext(HttpServer server,HttpServletRequest request,HttpServletResponse response,HashMap<String,String> parameters)
	{
		this.server=server;
		this.request=request;
		this.response=response;
		this.parameters=parameters;
		this.userResponse=false;
	}
	public HttpServletRequest getRequest() {
		return request;
	}
	public HttpServletResponse getResponse() {
		//this.userResponse=true;
		return response;
	}
	public HashMap<String, String> getParameters() {
		return parameters;
	}
	public String getParameter(String name)
	{
		return parameters.get(name);
	}

	String requestContentText=null;
	static final float BEST_GUESS_COMPRESSED_RATIO=4.0f; //Based on empical evidence. YMMV. Consider making this configurable for non-compressible content (video, etc).
	static final int BEST_GUESS_AVERAGE_COMPRESSED_LENGTH=4000; //Based on empical evidence. YMMV. Consider making this configurable for really large files (video, etc). 

	ByteArrayStreamContent requestContent=null;

	public OutputStream getOutputStream() throws IOException
	{
        this.userResponse=true;
        String encoding=request.getHeader("Accept-Encoding");
        if ((encoding!=null)&&(encoding.indexOf("gzip")>=0))
        {
            response.addHeader(HTTP.CONTENT_ENCODING, "gzip");
            return new GZIPOutputStream(response.getOutputStream());
        }
        else
        {
            return response.getOutputStream();
        }
	}
	private Utils.ByteArrayStreamContent getRequestContent() throws IOException
	{
		if (requestContent==null)
		{
	        int contentLength=request.getContentLength();
            String contentType=request.getHeader("Content-Type");
            if ((contentType!=null)&&StringUtils.contains(contentType, "application/x-www-form-urlencoded"))
            {
                //Don't quite like this. Jetty should still allow the content to be read, even thought it had to use the content to form the 
                //query string. As an alternative, Jetty could have returned the content as the query string as a compromise, but actually returns null for the query string. 
                //Thus, there is no way for us to get to the query string for logging purposes.
                
                requestContent=new ByteArrayStreamContent(null,0);
                return requestContent;
            }
			String encoding=request.getHeader("Content-Encoding");
			if ((encoding!=null)&&encoding.startsWith("gzip")) //todo: Some java dude needs to extend this with other gzip subformats
			{
			    compressed=true;
                GZIPInputStream inputStream=new GZIPInputStream(request.getInputStream());
			    if (contentLength>0)
			    {
    				int guest=(int)(contentLength*BEST_GUESS_COMPRESSED_RATIO); //Guest the uncompressed length. 
                    requestContent=Utils.readToEnd(inputStream, guest);
			    }
				else if (contentLength<0)
				{
					//Chunked
                    requestContent=Utils.readToEnd(inputStream, BEST_GUESS_AVERAGE_COMPRESSED_LENGTH);
				}
			    else
			    {
				    requestContent=new ByteArrayStreamContent(null,0);
			    }
			}
			else if (contentLength>0)
			{
			    requestContent=new ByteArrayStreamContent(Utils.read(request.getInputStream(), contentLength),contentLength);
			}
			else if (contentLength<0)
			{
					//Chunked
                requestContent=Utils.readToEnd(request.getInputStream(), BEST_GUESS_AVERAGE_COMPRESSED_LENGTH);
			}
			else
			{
			    requestContent=new ByteArrayStreamContent(null,0);
			}
		}
		return requestContent;
	}
	public String getRequestContentText() throws IOException
	{
		if (requestContentText==null)
		{
		    Utils.ByteArrayStreamContent content=getRequestContent();
			if (content!=null)
			{
			    byte[] body = content.getContent();
				requestContentText=new String(body == null ? new byte[] {} : body,0,content.getSize(),Utils.getCharset(request.getContentType())); //Need to detect this.
			}
		}
		return requestContentText;
	}
	// return the uncompressed content length 
	// this is an internal profile method. 
    // if return value is negative, it means the request is compressed.
	int getContentLength()
	{
	    int length;
        try
        {
            length = getRequestContentText().length();
        }
        catch (IOException e)
        {
            return 0;
        }
	    if (compressed)
	    {
	        return -length;
	    }
	    return length;
	}
	public void setHeader(String name,String value)
	{
	    this.response.setHeader(name, value);
	}
	public void setStatusCode(int statusCode)
	{
		responseStatusCode=statusCode;
	}
	public void sendText(String text,String contentType,int statusCode)
	{
		responseText=text;
		responseStatusCode=statusCode;
		responseContentType=contentType;
	}
    public void sendText(String text,String contentType)
    {
        sendText(text,contentType,200,null);
    }
    public void sendText(String text,String contentType,int statusCode, KeyValue<String,String>[] headers)
    {
        responseText=text;
        responseStatusCode=statusCode;
        responseContentType=contentType;
        if (headers!=null)
        {
            for (KeyValue<String,String> header:headers)
            {
                this.response.setHeader(header.getKey(), header.getValue());
            }
        }
    }
    
	public void sendHtml(String text)
	{
	  //  this.userResponse = true;
		sendText(text,"text/html; charset=utf-8");
	}
	public void sendXml(String text)
	{
		sendText(text,"text/xml; charset=utf-8");
	}
}
